package com.misc.tunewsreader;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.markupartist.android.widget.ActionBar;
import com.markupartist.android.widget.ActionBar.AbstractAction;
import com.markupartist.android.widget.ActionBar.IntentAction;

public class TUNewsReaderActivity extends Activity {
	private SectionListAdapter adapter;
	private List<NNTPGroup> all_groups;
	public static final int Menu_settings = Menu.FIRST + 1;
	public static MenuItem m_settings;
	private ActionBar actionBar;
	private Parcelable state = null;
	private static Activity app;

	private static final int Menu_asread = Menu.FIRST + 1;
	private static final int Menu_asunread = Menu.FIRST + 2;
	private static final int Menu_reset = Menu.FIRST + 3;
	private static final int Menu_unsubscribe = Menu.FIRST + 4;
	private static final int Menu_cleanup = Menu.FIRST + 5;
	private static final Pattern mail_check = Pattern
			.compile("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?");

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onCreate(android.os.Bundle)
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		app = this;

		actionBar = (ActionBar) findViewById(R.id.actionbar);
		actionBar.setTitle(this.getString(R.string.newsgroups));
		actionBar.setProgressBarVisibility(View.VISIBLE);

		NNTPClient.connect(this, "news.tugraz.at", 119);
		if (!NNTPClient.isOnline()) {
			Toast.makeText(this, this.getString(R.string.switch_to_offline),
					Toast.LENGTH_SHORT).show();
			NNTPClient.setOfflineMode(true);
		} else {
			NNTPClient.setOfflineMode(false);
		}

	}

	/**
	 * Class for action bar to refresh group view
	 * 
	 */
	public static class RefreshAction extends AbstractAction {
		private Context mContext;

		public RefreshAction(Context context, int drawable) {
			super(drawable);
			mContext = context;
		}

		@Override
		public void performAction(View view) {
			// check if we have a connection
			((TUNewsReaderActivity) mContext).checkForConnection();

			// show progress indicator
			((TUNewsReaderActivity) mContext).actionBar
					.setProgressBarVisibility(View.VISIBLE);

			Runnable viewGroup = new Runnable() {
				@Override
				public void run() {
					// save listview state
					((TUNewsReaderActivity) mContext).state = ((ListView) ((TUNewsReaderActivity) mContext)
							.findViewById(R.id.newsgroups))
							.onSaveInstanceState();
					((TUNewsReaderActivity) mContext).loadFavoriteGroups();
					((TUNewsReaderActivity) mContext)
							.runOnUiThread(((TUNewsReaderActivity) mContext).loadFav);
				}
			};
			Thread thread = new Thread(null, viewGroup, "LoadGroups");
			thread.start();
		}
	}

	/**
	 * Class for action bar to show offline mode icon (and to reconnect on
	 * click)
	 */
	public static class OfflineModeAction extends AbstractAction {
		private Context mContext;

		public OfflineModeAction(Context context, int drawable) {
			super(drawable);
			mContext = context;
		}

		@Override
		public void performAction(View view) {
			if (NNTPClient.isOnline()) {
				Toast.makeText(mContext,
						mContext.getString(R.string.trying_reconnect),
						Toast.LENGTH_SHORT).show();
				NNTPClient.reconnect();
				if (NNTPClient.isOnline()) {
					NNTPClient.setOfflineMode(false);
					try {
						((TUNewsReaderActivity) app).refreshApp();
						((TUNewsReaderActivity) app).checkForConnection();
					} catch (Exception e) {
					}
				}
			} else {
				Toast.makeText(mContext,
						mContext.getString(R.string.no_internet),
						Toast.LENGTH_SHORT).show();
			}
		}
	}

	/**
	 * Checks if the user is connected to the internet. Changes the icons in the
	 * action bar according to the online status.
	 */
	public void checkForConnection() {
		// check if connected
		boolean is_offline = NNTPClient.isOfflineMode();

		Log.d("NNTP", "Check if online: " + (is_offline ? "true" : "false"));

		actionBar.removeAllActions();
		if (!is_offline)
			actionBar
					.addAction(new RefreshAction(this, R.drawable.bar_refresh));
		actionBar.addAction(new IntentAction(this, new Intent(this,
				Preferences.class), R.drawable.bar_settings));

		// managing groups is only possible in online mode
		if (!is_offline) {
			actionBar.addAction(new IntentAction(this,
					createManageIntent(this), R.drawable.bar_new1));
		} else {
			actionBar.addAction(new OfflineModeAction(this,
					R.drawable.bar_warning));
		}

	}

	/**
	 * Does a connection check, checks if the user has entered valid user name
	 * and email and reloads the groups.
	 */
	public void refreshApp() {
		// check if connected
		checkForConnection();

		// we have no username -> open settings
		checkForUsername();

		// show progress indicator
		actionBar.setProgressBarVisibility(View.VISIBLE);

		Runnable viewGroup = new Runnable() {
			@Override
			public void run() {
				loadFavoriteGroups();
				runOnUiThread(loadFav);
			}
		};
		Thread thread = new Thread(null, viewGroup, "LoadGroups");
		thread.start();

		// sync
		final SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(getApplicationContext());
		Runnable syncRun = new Runnable() {
			@Override
			public void run() {
				List<String> read = Sync.getReadMessages(prefs);
				for (String id : read) {
					String[] info = id.split(";");
					if (info.length != 2)
						continue;
					Log.d("READ", id);
					NNTPClient.markPostReadById(info[0], info[1].equals("1"));
				}
			}
		};
		Thread syncThread = new Thread(null, syncRun, "SyncMessages");
		syncThread.start();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onResume()
	 */
	@Override
	protected void onResume() {
		super.onResume();
		refreshApp();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onPause()
	 */
	@Override
	protected void onPause() {
		super.onPause();
		// save listview state
		state = ((ListView) findViewById(R.id.newsgroups))
				.onSaveInstanceState();
		// clear adapter
		ListView group_list = (ListView) findViewById(R.id.newsgroups);
		group_list.setAdapter(null);
	}
	
	@Override
	protected void onRestart() {
		super.onRestart();
		// reconnect
		NNTPClient.connect(this, "news.tugraz.at", 119);
		if (!NNTPClient.isOnline()) {
			Toast.makeText(this, this.getString(R.string.switch_to_offline),
					Toast.LENGTH_SHORT).show();
			NNTPClient.setOfflineMode(true);
		} else {
			NNTPClient.setOfflineMode(false);
		}
		refreshApp();
	}

	/**
	 * Thread that displays the list of groups
	 */
	private Runnable loadFav = new Runnable() {
		@Override
		public void run() {
			// if no groups, show help text
			if (adapter.getCount() == 0) {
				((TextView) findViewById(R.id.add_helper))
						.setVisibility(View.VISIBLE);
			} else {
				((TextView) findViewById(R.id.add_helper))
						.setVisibility(View.GONE);
			}

			// display groups
			ListView group_list = (ListView) findViewById(R.id.newsgroups);
			group_list.setAdapter(adapter);
			group_list.setOnItemClickListener(enterGroup);
			registerForContextMenu(group_list);
			// restore listview state
			if (state != null) {
				((ListView) findViewById(R.id.newsgroups))
						.onRestoreInstanceState(state);
			}
			// hide progress bar
			actionBar.setProgressBarVisibility(View.GONE);
		}
	};

	/**
	 * Checks whether a given mail address has a valid syntax
	 * 
	 * @param mail
	 *            the mail address as string
	 * @return true if syntax is correct, false otherwise
	 */
	boolean validMail(String mail) {
		Matcher matcher = mail_check.matcher(mail);
		return matcher.matches();
	}

	/**
	 * Goes to preferences screen if the user has not entered an user name or a
	 * valid email address
	 */
	void checkForUsername() {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(getApplicationContext());
		String user = prefs.getString("pref_username", null);
		String mail = prefs.getString("pref_mail", null);
		if (user == null || mail == null || user.length() == 0
				|| mail.length() == 0 || !validMail(mail.toLowerCase())) {
			Toast.makeText(
					getApplicationContext(),
					getApplicationContext().getString(
							R.string.name_mail_required), Toast.LENGTH_SHORT)
					.show();
			final Intent intent = new Intent(this, Preferences.class);
			startActivityForResult(intent, 0);
		}

	}

	/**
	 * Loads the list of subscribed groups
	 */
	void loadFavoriteGroups() {
		GroupManager fav = new GroupManager(this);
		List<String> groups = fav.getFavoriteList();

		// load all groups
		all_groups = NNTPClient.getGroups();

		// generate listview of favorite groups
		ArrayList<ListItem> items = fav.generateListView(groups, all_groups);

		// set adapter
		adapter = new SectionListAdapter(this, items);
		adapter.notifyDataSetChanged();
	}

	/**
	 * Creates the intent to show the list for (un-)subscribing to/from
	 * newsgroups
	 * 
	 * @param context
	 *            Application context
	 * @return the intent to start
	 */
	private Intent createManageIntent(Context context) {
		Intent i = new Intent(context, GroupView.class);
		i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
		return i;
	}

	/**
	 * Listener to handle the touch on a group name. Enters the clicked group.
	 */
	OnItemClickListener enterGroup = new OnItemClickListener() {
		@SuppressWarnings("deprecation")
		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position,
				long id) {

			boolean entered = false;
			String full_name = ((TextView) view
					.findViewById(R.id.list_item_full_text)).getText()
					.toString();
			if (all_groups != null) {
				for (int i = 0; i < all_groups.size(); i++) {
					if (all_groups.get(i).getName().equals(full_name)) {
						NNTPClient.enterGroup(all_groups.get(i));
						entered = true;
						break;
					}
				}
			} else {
				// we have no internet connection, try to enter anyway
				NNTPClient.enterGroupOffline(NNTPGroup.fromName(full_name));
				entered = true;
			}

			if (entered) {
				final Intent intent = new Intent(getApplicationContext(),
						ThreadView.class);

				startActivityForResult(intent, 0);
			} else {
				Toast.makeText(
						getApplicationContext(),
						getApplicationContext().getString(
								R.string.group_unavailable), Toast.LENGTH_SHORT)
						.show();
				refreshApp();
			}
		}
	};

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu,
	 * android.view.View, android.view.ContextMenu.ContextMenuInfo)
	 */
	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo) {
		if (v.getId() == R.id.newsgroups) {
			AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
			menu.setHeaderTitle(((ListEntry) adapter.getItem(info.position)).full_name);
			menu.add(Menu.NONE, Menu_asread, 0, getApplicationContext()
					.getString(R.string.mark_as_read));
			menu.add(Menu.NONE, Menu_asunread, 1, getApplicationContext()
					.getString(R.string.mark_as_unread));
			menu.add(Menu.NONE, Menu_reset, 2, getApplicationContext()
					.getString(R.string.reset_group));
			menu.add(Menu.NONE, Menu_unsubscribe, 3, getApplicationContext()
					.getString(R.string.unsubscribe_group));
			menu.add(Menu.NONE, Menu_cleanup, 4, getApplicationContext()
					.getString(R.string.clean_group));
		}
	}

	/**
	 * Reload the list of newsgroups
	 */
	private void reloadGroups() {
		Runnable viewGroup = new Runnable() {
			@Override
			public void run() {
				loadFavoriteGroups();
				runOnUiThread(loadFav);
			}
		};
		Thread thread = new Thread(null, viewGroup, "LoadGroups");
		thread.start();
	}

	/**
	 * asks the user how many posts should remain when cleaning up a newsgroup
	 * 
	 * @param group
	 *            The newsgroup to cleanup
	 */
	private void cleanupGroup(final String group) {
		LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		final View cleanup = inflater.inflate(R.layout.cleanup_ask, null);

		Builder cleanup_dlg = new AlertDialog.Builder(this)
				.setView(cleanup)
				.setTitle(NNTPClient.getCurrentGroup().getName())
				.setMessage("Cleanup posts")
				.setPositiveButton(getString(R.string.cleanup),
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int whichButton) {
								int limit = -1;
								limit = Integer.parseInt(((EditText) cleanup
										.findViewById(R.id.cleanup_count))
										.getText().toString());
								Log.d("NNTP", "Cleanup, keep " + limit
										+ " posts");
								NNTPClient.cleanupGroup(
										NNTPGroup.fromName(group), limit);
								Toast.makeText(
										getApplicationContext(),
										getApplicationContext().getString(
												R.string.cleanup_message)
												.replace("$posts",
														String.valueOf(limit)),
										Toast.LENGTH_SHORT).show();
								reloadGroups();
							}
						})
				.setNegativeButton(getString(R.string.cancel),
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int whichButton) {
								finish();
							}
						}).setCancelable(false);
		cleanup_dlg.show();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
	 */
	@Override
	public boolean onContextItemSelected(MenuItem item) {
		AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
				.getMenuInfo();
		String group = ((ListEntry) adapter.getItem(info.position)).full_name;
		String txt;

		// save listview state
		state = ((ListView) findViewById(R.id.newsgroups))
				.onSaveInstanceState();

		switch (item.getItemId()) {

		case Menu_asread:
			txt = getApplicationContext().getString(R.string.group_mark_read)
					.replace("$group", group);
			Toast.makeText(getApplicationContext(), txt, Toast.LENGTH_SHORT)
					.show();
			NNTPClient.markGroupRead(group, true);
			int only_server = NNTPClient.getNewPostsOnServer(group);
			Log.d("NNTP", "Can't mark " + only_server + " as read");
			if (only_server > 0)
				Toast.makeText(
						getApplicationContext(),
						getApplicationContext().getString(
								R.string.cant_mark_posts_as_read).replace(
								"$posts", String.valueOf(only_server)),
						Toast.LENGTH_SHORT).show();
			reloadGroups();

			break;

		case Menu_asunread:
			txt = getApplicationContext().getString(R.string.group_mark_unread)
					.replace("$group", group);
			Toast.makeText(getApplicationContext(), txt, Toast.LENGTH_SHORT)
					.show();
			NNTPClient.markGroupRead(group, false);
			reloadGroups();
			break;

		case Menu_reset:
			txt = getApplicationContext().getString(R.string.group_reset)
					.replace("$group", group);
			Toast.makeText(getApplicationContext(), txt, Toast.LENGTH_SHORT)
					.show();
			NNTPClient.resetGroup(NNTPGroup.fromName(group));
			reloadGroups();
			break;

		case Menu_unsubscribe:
			GroupManager man = new GroupManager(getApplicationContext());
			man.setFavorite(group, false);
			NNTPClient.resetGroup(NNTPGroup.fromName(group));
			reloadGroups();
			break;

		case Menu_cleanup:
			cleanupGroup(group);
			break;
		}

		return true;
	}
}
